//
//  Render3D.swift
//  videoPlayer
//
//  Created by xwl on 2019/2/26.
//  Copyright © 2019年 xuwanliang. All rights reserved.
//

import MetalKit

class Render3D: NSObject {
    var device: MTLDevice?
    var commandQueue: MTLCommandQueue! = nil
    var rps: MTLRenderPipelineState! = nil
    var vertexBuffer: MTLBuffer! = nil
    var colorSamplerState: MTLSamplerState! = nil
    
    var width:Int = 0
    var height:Int = 0
    
    public var interTexture1: MTLTexture?
    
    required init(device: MTLDevice, commandQueue: MTLCommandQueue, width: Int, height: Int){
        super.init()
        
        self.device = device
        self.commandQueue = commandQueue
        self.width = width
        self.height = height
        
        initCommon()
        
        initInterleaveBuffer(width: self.width, height: self.height)
        
    }
    
    public func initInterleaveBuffer(width: Int, height: Int){
        
        let size = width * height
        
        var data1 =  Array<UInt8>(repeating: 0, count: size)
        print("data1's cap: \(data1.capacity)")
        for i in 0..<height{
            for j in 0..<width{
                let pos = i * width + j
                data1[pos] = UInt8((j % 2) * 255)
            }
        }
        
        let descriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .r8Unorm, width: width, height: height, mipmapped: false)
        interTexture1 = device?.makeTexture(descriptor: descriptor)
        let region = MTLRegionMake2D(0, 0, width, height)
        interTexture1?.replace(region: region, mipmapLevel: 0, withBytes: data1, bytesPerRow: width)
        
    }
    
    func initCommon(){
        loadShaders()
    }
    
    func loadShaders(){
        
        let vertexData:[Float] =
            [-1.0, -1.0, 0.0, 1.0,
              1.0, -1.0, 1.0, 1.0,
             -1.0,  1.0, 0.0, 0.0,
              1.0, -1.0, 1.0, 1.0,
              1.0,  1.0, 1.0, 0.0,
             -1.0,  1.0, 0.0, 0.0]
        let dataSize = vertexData.count * MemoryLayout<Float>.size
        vertexBuffer = device?.makeBuffer(bytes: vertexData, length: dataSize, options: [])
        let library = device?.makeDefaultLibrary()!
        let vertex_func = library?.makeFunction(name: "vertex_func")
        let frag_func = library?.makeFunction(name: "fragment_func")
        let rpld = MTLRenderPipelineDescriptor()
        rpld.vertexFunction = vertex_func
        rpld.fragmentFunction = frag_func
        rpld.colorAttachments[0].pixelFormat = .bgra8Unorm
        do{
            try rps = device?.makeRenderPipelineState(descriptor: rpld)
        }catch let error{
            fatalError("\(error)")
        }
        
        let samplerDescriptor = MTLSamplerDescriptor()
        samplerDescriptor.minFilter = .linear
        samplerDescriptor.magFilter = .linear
        samplerDescriptor.sAddressMode = .repeat
        samplerDescriptor.tAddressMode = .repeat
        self.colorSamplerState = self.device?.makeSamplerState(descriptor: samplerDescriptor)
        
    }
    
    public func render(drawable: CAMetalDrawable!, rpd: MTLRenderPassDescriptor!, texture: MTLTexture!){
        
        rpd.colorAttachments[0].clearColor = MTLClearColor(red: 0.5, green: 0.0, blue: 0.11, alpha: 1.0)
        let commandBuffer = commandQueue!.makeCommandBuffer()
        let commandEncode = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd)
        //commandEncode?.setViewport(MTLViewport(originX: 0, originY: 0, width: 640, height: 480, znear: -1, zfar: 1))
        commandEncode?.setRenderPipelineState(rps!)
        commandEncode?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
        
        commandEncode?.setFragmentTexture(texture, index: 0)
        commandEncode?.setFragmentTexture(interTexture1, index: 1)
        commandEncode?.setFragmentSamplerState(self.colorSamplerState, index: 0)
        
        commandEncode?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6, instanceCount: 1)
        commandEncode?.endEncoding()
        commandBuffer?.present(drawable)
        commandBuffer?.commit()
    }
}
